/**
 * JarClassLoader.java
 */
package com.pluginsacceptor.acceptor.activator.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;


/**
 * The class implements jar class loader
 */
public class JarClassLoader extends SecureClassLoader {

    /** */
    private final ZipFile file;

    private final List<String> cash;

    /**
     * Constructor
     * @param filename the file for load
     * @throws IOException if IO error has occurred
     */
    public JarClassLoader(final String filename) throws IOException {
        this.file = new ZipFile(filename);
        final Enumeration<? extends ZipEntry> entries = this.file.entries();

        this.cash = new ArrayList<String>();

        while (entries.hasMoreElements()) {
            final String enEntry = entries.nextElement().getName();
            cash.add(enEntry);
        }
    }

    @Override
    public synchronized Class loadClass(final String name) throws ClassNotFoundException   {

        try {

//            System.out.println("loadClass (" + name + ")");
            final String normalizeName = ClassUtils.packageToFileNotation(name);
            if (!cash.contains(normalizeName)) {
//                System.out.println("loadClass (" + name + ") from system");
                return super.findSystemClass(name);
            }


            final ZipEntry entry = this.file.getEntry(normalizeName);

            if (entry == null) {
                throw new ClassNotFoundException(normalizeName);
            }
            final ByteArrayOutputStream out = loadClassData(entry);
            final String defineClassName = ClassUtils.fileToPackageNotation(normalizeName);
         //   cash.remove(normalizeName);
            return  defineClass(defineClassName, out.toByteArray(), 0, out.size());
        } catch (final IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException(e.getMessage(), e);
        }
    }

    /**
     * Load class data
     * @param entry the source entry
     * @return data
     * @throws IOException if some IO error occurred
     */
    private ByteArrayOutputStream loadClassData(final ZipEntry entry) throws IOException {
        final byte[] array = new byte[1024];
        final InputStream in = this.file.getInputStream(entry);
        final ByteArrayOutputStream out = new ByteArrayOutputStream(array.length);
        int length;
        while ((length = in.read(array)) > 0) {
            out.write(array, 0, length);
        }
        return out;
    }
}